#include "env.h"

#include <stdlib.h>
#include <stdint.h>
#define byte uint8_t

el_env::el_env (size_t heap_size, size_t alignment)
{
	heap = malloc (heap_size);
	hs = heap_size;
	align = alignment;
}

el_env::~el_env()
{
	if (heap) free (heap);
}

int el_env::register_type (const el_type& t)
{

	return 0;
}

int el_env::register_c_proc (const string& name, el_c_proc proc_body)
{

	return 0;
}

el_var* el_env::eval (el_var* expression)
{

	return NULL;
}

void * el_env::new_heap_object (size_t size)
{
	set<el_heap_entry>::iterator i;
	el_heap_entry he;
	void* t;

	//do some alignment
	if (size % align) {
		size /= align;
		++size;
		size *= align;
	}

	for (i = free_space.begin();i != free_space.end();++i)
		if (i->size >= size) {

			he = *i;

			//remove old free space entry
			free_space.erase (i);

			//calculate the pointer
			t = (byte*) heap + he.start;

			//decrease free space size
			he.size -= size;
			if (he.size) { //if something remains
				he.start += size; //move it and push it back
				free_space.insert (he);
			}
			return t; //finish searching
		}

	return NULL;  //too bad, none found. we shall collect.
}

void * el_env::allocate (size_t size)
{
	//Try to Allocate, then try to sweep and allocate, then die.
	void*d;
	d = new_heap_object (size);
	if (d) return d;
	collect_garbage();
	d = new_heap_object (size);
	if (d) return d;
	else return NULL;
}

void el_env::deallocate (void* p)
{
	set<el_heap_entry>::iterator i;
	i = allocated_space.find
	    (el_heap_entry ( (byte*) p - (byte*) heap, 0) );
	//note. heap entries are indexed by start, not by size,
	//so the zero here doesn't harm anything.
	if (i != allocated_space.end() ) free_space.insert (*i);
	allocated_space.erase (i);
}

el_var * el_env::new_var (int type, size_t data_size)
{
	el_var* var;
	void* data;
	data = allocate (data_size);
	if (!data) return NULL;
	
	var = (el_var*) allocate (sizeof (el_var) );

	if (!var) {
		deallocate (data);
		return NULL;
	}

	var = new (var) el_var; //call the constructor

	var->type = type;
	var->data = data;

	collector_queue.push_back (var);

	return var;
}

void el_env::free_var (el_var* p)
{
	p->el_var::~el_var(); //destruct it (if possible)
	//TODO, check whether ^it^ is really needed
	
	deallocate(p->data);
	deallocate(p);
}

void el_env::mark_collectable(el_var* p)
{
	el_var*t=NULL;
	int i;

	p->flags &= (~V_NOFREE);
	for(i=0;t=type[p->type].get_children(p->data,i);++i)
		mark_collectable(t);
}

void el_env::sort_out_free_space()
{
	/*
	 * This "defragments" free space list by joining more gapless free
	 * spaces into bigger ones, allowing us to choose better pieces.
	 */

	set<el_heap_entry>::iterator i, j;
	el_heap_entry eh;

	//free_space should always have at least 1 entry.
	if (!free_space.size() ) return;

	/*
	 * An extremely ugly hack follows. (we would need operator+ in 
	 * std::set iterators to get rid of it)
	 * I hope that gcc's -O2 is able to simplify those j=++i=j...
	 */

	i = free_space.begin();
	j = i;
	++j;
	while (1) {
		if (j == free_space.end() ) break;

		//if the space touches the following, join them
		//(we also count overlapping, but this should never happen.
		if ( (i->start + i->size) >= (j->start) ) {
			eh = *i; //compute a new field
			eh.start = i->start;
			eh.size = j->start + j->size - i->start;
			free_space.erase (i); //erase old
			free_space.erase (j);
			i = free_space.insert (eh).first; //insert the new one
			j = i;
			++j;  //^^ oh das iterator stinx!
		} else {
			i = j;
			++j;
		}
	}
}

void el_env::collect_garbage ()
{
	set<el_var*> processing, active;
	list<el_var*>::iterator i;

	for (i = collector_queue.begin();i != collector_queue.end();++i)
		collector.insert (*i);
	collector_queue.clear();



	sort_out_free_space();
}

